package com.elvis.chat.utils.add;

import org.jivesoftware.smack.packet.IQ;
import org.jivesoftware.smack.util.StringUtils;

import android.os.Parcel;
import android.os.Parcelable;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class AttachmentInfo extends IQ implements Parcelable {

    /** Parcelable.Creator needs by Android. */
    public static final Parcelable.Creator<AttachmentInfo> CREATOR = new Parcelable.Creator<AttachmentInfo>() {

	@Override
	public AttachmentInfo createFromParcel(Parcel source) {
	    // TODO Auto-generated method stub
	    return new AttachmentInfo(source);
	}

	@Override
	public AttachmentInfo[] newArray(int size) {
	    // TODO Auto-generated method stub
	    return new AttachmentInfo[size];
	}

    };

    private long id;
    private String resourceId;
    private String fileName;
    private long fileSize = 0;
    private int fileStatus = 0;
    private String description;
    private String mimeType;
    private String filePath;
    private long audioLength = 0;

    private String hashKey;
    private String hashValue;
    private String binval;

    public AttachmentInfo() {
    }

    public long getId() {
	return id;
    }

    public void setId(long id) {
	this.id = id;
    }

    public String getResourceId() {
	return resourceId;
    }

    public void setResourceId(String resourceId) {
	this.resourceId = resourceId;
    }

    public String getFileName() {
	return fileName;
    }

    public void setFileName(String fileName) {
	this.fileName = fileName;
    }

    public long getFileSize() {
	return fileSize;
    }

    public void setFileSize(long fileSize) {
	this.fileSize = fileSize;
    }

    public int getFileStatus() {
	return fileStatus;
    }

    public void setFileStatus(int fileStatus) {
	this.fileStatus = fileStatus;
    }

    public String getDescription() {
	return description;
    }

    public void setDescription(String description) {
	this.description = description;
    }

    public String getMimeType() {
	return mimeType;
    }

    public void setMimeType(String mimeType) {
	this.mimeType = mimeType;
    }

    public String getHashKey() {
	return hashKey;
    }

    public void setHashKey(String hashKey) {
	this.hashKey = hashKey;
    }

    public String getHashValue() {
	return hashValue;
    }

    public void setHashValue(String hashValue) {
	this.hashValue = hashValue;
    }

    public long getAudioLength() {
	return audioLength;
    }

    public void setAudioLength(long audioLength) {
	this.audioLength = audioLength;
    }

    public String getFilePath() {
	return filePath;
    }

    public void setFilePath(String filePath) {
	this.filePath = filePath;
    }

    /**
     * Set the binval for the MediaMessage by specifying the url to the file.
     * @param binvalURL the url to the file
     */
    public void setBinval(URL binvalURL) {
	byte[] bytes = new byte[0];
	try {
	    bytes = getBytes(binvalURL);
	} catch (IOException e) {
	    e.printStackTrace();
	}

	setBinval(bytes);
    }

    /**
     * Specify the bytes for the binval to use.
     * @param bytes the bytes of the binval.
     */
    public void setBinval(byte[] bytes) {
	// Otherwise, add to mappings.
	String encodedImage = StringUtils.encodeBase64(bytes);
	this.binval = encodedImage;
    }

    /**
     * Specify the bytes for the binval to use.
     * @param bytes the bytes of the binval.
     */
    public void setBinval(String binval) {
	// Otherwise, add to mappings.
	this.binval = binval;
    }

    /**
     * Common code for getting the bytes of a url.
     * @param url the url to read.
     */
    public static byte[] getBytes(URL url) throws IOException {
	final String path = url.getPath();
	final File file = new File(path);
	if (file.exists()) {
	    return getFileBytes(file);
	}

	return null;
    }

    private static byte[] getFileBytes(File file) throws IOException {
	BufferedInputStream bis = null;
	try {
	    bis = new BufferedInputStream(new FileInputStream(file));
	    int bytes = (int) file.length();
	    byte[] buffer = new byte[bytes];
	    int readBytes = bis.read(buffer);
	    if (readBytes != buffer.length) {
		throw new IOException("Entire file not read");
	    }
	    return buffer;
	} finally {
	    if (bis != null) {
		bis.close();
	    }
	}
    }

    public byte[] getBinval() {
	if (binval == null) {
	    return null;
	}
	return StringUtils.decodeBase64(binval);
    }

    /**
     * Returns the SHA-1 Hash of the Avatar image.
     * @return the SHA-1 Hash of the Avatar image.
     */
    public String getBinValHash() {
	byte[] bytes = getBinval();
	if (bytes == null) {
	    return null;
	}

	MessageDigest digest;
	try {
	    if (hashKey == null)
		hashKey = "SHA-1";
	    digest = MessageDigest.getInstance(hashKey);
	} catch (NoSuchAlgorithmException e) {
	    e.printStackTrace();
	    return null;
	}

	digest.update(bytes);
	return StringUtils.encodeHex(digest.digest());
    }

    public String getChildElementXML() {
	StringBuilder sb = new StringBuilder();
	new AttachmentWriter(sb).write();
	return sb.toString();
    }

    public static boolean checkIsMedia(String message) {
	if (message != null && message.indexOf(AttachmentExtension.ELEMENT_NAME) != -1) {
	    return true;
	}
	return false;
    }

    private boolean hasContent() {
	//noinspection OverlyComplexBooleanExpression

	return fileName != null || description != null || mimeType != null || hashKey != null || hashValue != null
	    || binval != null;
    }

    // Used in tests:

    public boolean equals(Object o) {
	if (this == o)
	    return true;
	if (o == null || getClass() != o.getClass())
	    return false;

	final AttachmentInfo mediaMessage = (AttachmentInfo) o;

	if (fileName != null ? !fileName.equals(mediaMessage.fileName) : mediaMessage.fileName != null) {
	    return false;
	}
	if (fileSize != mediaMessage.fileSize) {
	    return false;
	}
	if (description != null ? !description.equals(mediaMessage.description) : mediaMessage.description != null) {
	    return false;
	}
	if (mimeType != null ? !mimeType.equals(mediaMessage.mimeType) : mediaMessage.mimeType != null) {
	    return false;
	}
	if (hashKey != null ? !hashKey.equals(mediaMessage.hashKey) : mediaMessage.hashKey != null) {
	    return false;
	}
	if (hashValue != null ? !hashValue.equals(mediaMessage.hashValue) : mediaMessage.hashValue != null) {
	    return false;
	}
	return true;

    }

    public int hashCode() {
	int result;
	result = fileName.hashCode();
	result = 29 * result + (description != null ? description.hashCode() : 0);
	result = 29 * result + (mimeType != null ? mimeType.hashCode() : 0);
	result = 29 * result + (hashKey != null ? hashKey.hashCode() : 0);
	result = 29 * result + (hashValue != null ? hashValue.hashCode() : 0);
	return result;
    }

    public String toString() {
	return getChildElementXML();
    }

    //==============================================================

    private class AttachmentWriter {

	private final StringBuilder sb;

	AttachmentWriter(StringBuilder sb) {
	    this.sb = sb;
	}

	public void write() {
	    appendTag(AttachmentExtension.ELEMENT_NAME, "xmlns", AttachmentExtension.NAMESPACE, hasContent(),
		new ContentBuilder() {
		    public void addTagContent() {
			buildActualContent();
		    }
		});
	}

	private void buildActualContent() {
	    appendTag("FILENAME", StringUtils.escapeForXML(fileName));
	    appendTag("FILESIZE", StringUtils.escapeForXML(new Long(fileSize).toString()));
	    appendTag("DESCRIPTION", StringUtils.escapeForXML(description));
	    appendTag("MIMETYPE", StringUtils.escapeForXML(mimeType));
	    appendTag("AUDIOLENGTH", StringUtils.escapeForXML(new Long(audioLength).toString()));
	    appendTag("HASHKEY", StringUtils.escapeForXML(hashKey));
	    appendTag("HASHVALUE", StringUtils.escapeForXML(hashValue));
	    appendTag("BINVAL", StringUtils.escapeForXML(binval));
	}

	private void appendEmptyTag(Object tag) {
	    sb.append('<').append(tag).append("/>");
	}

	private void appendTag(String tag, String attr, String attrValue, boolean hasContent, ContentBuilder builder) {
	    sb.append('<').append(tag);
	    if (attr != null) {
		sb.append(' ').append(attr).append('=').append('\'').append(attrValue).append('\'');
	    }

	    if (hasContent) {
		sb.append('>');
		builder.addTagContent();
		sb.append("</").append(tag).append(">\n");
	    } else {
		sb.append("/>\n");
	    }
	}

	private void appendTag(String tag, boolean hasContent, ContentBuilder builder) {
	    appendTag(tag, null, null, hasContent, builder);
	}

	private void appendTag(String tag, final String tagText) {
	    if (tagText == null)
		return;
	    final ContentBuilder contentBuilder = new ContentBuilder() {
		public void addTagContent() {
		    sb.append(tagText.trim());
		}
	    };
	    appendTag(tag, true, contentBuilder);
	}

    }

    //==============================================================

    private interface ContentBuilder {

	void addTagContent();
    }

    @Override
    public int describeContents() {
	// TODO Auto-generated method stub
	return 0;
    }

    /**
     * Construct a UserInfo from a parcel.
     * @param in parcel to use for construction
     */
    private AttachmentInfo(final Parcel in) {
	id = in.readLong();
	resourceId = in.readString();
	fileName = in.readString();
	fileSize = in.readLong();
	fileStatus = in.readInt();
	filePath = in.readString();
	description = in.readString();
	mimeType = in.readString();
	audioLength = in.readLong();

	hashKey = in.readString();
	hashValue = in.readString();
	binval = in.readString();
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
	dest.writeLong(id);
	dest.writeString(resourceId);
	dest.writeString(fileName);
	dest.writeLong(fileSize);
	dest.writeInt(fileStatus);
	dest.writeString(filePath);
	dest.writeString(description);
	dest.writeString(mimeType);
	dest.writeLong(audioLength);

	dest.writeString(hashKey);
	dest.writeString(hashValue);
	dest.writeString(binval);
    }

    @Override
    public String toXML() {
	// TODO Auto-generated method stub
	return getChildElementXML();
    }

    //==============================================================

}
